package com.todo.sight.application.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.common.collect.Lists;
import com.todo.common.common.entity.response.page.PageResult;
import com.todo.common.common.enums.BizErrorCodeEnum;
import com.todo.common.common.enums.CityTypeEnum;
import com.todo.common.common.exception.BizException;
import com.todo.common.utils.StringUtils;
import com.todo.component.redis.RedisService;
import com.todo.component.redis.cache.LocalCacheHelper;
import com.todo.sight.common.base.SightKeyBuild;
import com.todo.sight.application.entity.condition.CityInfoConditionBO;
import com.todo.sight.application.convert.SightServiceConvert;
import com.todo.sight.application.entity.bo.CityInfoBO;
import com.todo.sight.infrastructure.entity.condition.CityInfoCondition;
import com.todo.sight.infrastructure.entity.po.CityInfoPO;
import com.todo.sight.infrastructure.repository.CityInfoRepository;
import com.todo.sight.application.service.CityInfoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * (CityInfo)表服务实现类
 *
 * @author zhangwenxiang
 * @since 2024-03-04 00:19:44
 */
@Slf4j
@Service("cityInfoService")
public class CityInfoServiceImpl implements CityInfoService {

    /**
     * 默认使用Caffeine 本地缓存
     * 注：初期暂不考虑冷启动问题造成的缓存穿透
     */
    public static final Cache<String, CityInfoBO> CITY_LOCAL_CACHE = Caffeine.newBuilder()
            .maximumSize(4000) // 设置缓存最大容量为100
            .expireAfterWrite(5, TimeUnit.MINUTES) // 设置缓存过期时间为10分钟
            .build();

    @Resource
    private CityInfoRepository cityInfoRepository;
    @Resource
    private RedisService redisService;

    @Override
    public Boolean insertCityInfo(CityInfoBO cityInfoBO) {
        if (cityInfoBO == null) {
            return false;
        }

        if (!CityTypeEnum.PROVINCE.equals(cityInfoBO.getType()) && cityInfoBO.getParentId() == null) {
            log.warn("CityInfoService#insertCityInfo 新增城市信息失败，城市无上级Id cityInfoBO：【{}】", JSONObject.toJSONString(cityInfoBO));
            throw new BizException(BizErrorCodeEnum.PARAM_ERROR);
        }
        //保存省份信息，默认父级Id为1
        if (CityTypeEnum.PROVINCE.equals(cityInfoBO.getType())) {
            cityInfoBO.setParentId(1L);
        }

        //判断名称是否重复
        CityInfoBO alreadyCityInfoBO = queryCityInfoByName(cityInfoBO.getName());
        if (alreadyCityInfoBO != null) {
            log.warn("CityInfoService#insertCityInfo 新增城市信息失败，当前名称已存在 cityInfoBO：【{}】", JSONObject.toJSONString(cityInfoBO));
            throw new BizException(BizErrorCodeEnum.SUCCESS_EXIST, "当前名称已存在");
        }
        CityInfoPO cityInfoPO = SightServiceConvert.convertToCityInfoPO(cityInfoBO);
        Boolean inserted = cityInfoRepository.insertCityInfoPO(cityInfoPO);
        if (inserted) {
            CityInfoBO savedCityInfoBO = SightServiceConvert.convertToCityInfoBO(cityInfoPO);
            //由于是本地缓存，所以清除的只能是本机的缓存，但是整体时间为5min 基本不影响
            CITY_LOCAL_CACHE.put(LocalCacheHelper.getCityInfoBOKey(cityInfoBO.getId()), savedCityInfoBO);
            //保存当前信息缓存，保存父节点set信息
            String detailKey = SightKeyBuild.cityDetailKey(cityInfoPO.getId());
            redisService.set(detailKey, JSONObject.toJSONString(savedCityInfoBO));
            String parentIdKey = SightKeyBuild.cityParentIdListKey(cityInfoPO.getParentId());
            redisService.sAdd(parentIdKey, String.valueOf(cityInfoPO.getId()));
        }
        return inserted;
    }

    @Override
    public Boolean updateCityInfo(CityInfoBO cityInfoBO) {
        if (cityInfoBO == null || cityInfoBO.getId() == null) {
            return false;
        }

        if (!CityTypeEnum.PROVINCE.equals(cityInfoBO.getType()) && cityInfoBO.getParentId() == null) {
            log.warn("CityInfoService#updateCityInfo 更新城市信息失败，城市无上级Id cityInfoBO：【{}】", JSONObject.toJSONString(cityInfoBO));
            throw new BizException(BizErrorCodeEnum.PARAM_ERROR);
        }
        //保存省份信息，默认父级Id为1
        if (CityTypeEnum.PROVINCE.equals(cityInfoBO.getType())) {
            cityInfoBO.setParentId(1L);
        }
        CityInfoBO alreadyCityInfoBO = queryCityInfoById(cityInfoBO.getId());
        if (alreadyCityInfoBO == null) {
            log.warn("CityInfoService#updateCityInfo 更新城市信息失败，当前Id不存在 cityInfoBO：【{}】", JSONObject.toJSONString(cityInfoBO));
            throw new BizException(BizErrorCodeEnum.DATA_NOT_EXIST);
        }
        Boolean updateResult = cityInfoRepository.updateCityInfoPOById(SightServiceConvert.convertToCityInfoPO(cityInfoBO));
        if (updateResult) {
            //由于是本地缓存，所以清除的只能是本机的缓存，但是整体时间为5min 基本不影响
            CITY_LOCAL_CACHE.invalidate(LocalCacheHelper.getCityInfoBOKey(cityInfoBO.getId()));
            //删除当前信息缓存，保存父节点set信息
            String detailKey = SightKeyBuild.cityDetailKey(cityInfoBO.getId());
            redisService.del(detailKey);
            String parentIdKey = SightKeyBuild.cityParentIdListKey(cityInfoBO.getParentId());
            redisService.sRemove(parentIdKey, String.valueOf(cityInfoBO.getId()));
        }
        return updateResult;
    }

    @Override
    public Boolean removeCityInfoById(Long id) {
        if (id == null || id < 0) {
            throw new BizException(BizErrorCodeEnum.PARAM_ERROR);
        }
        CityInfoBO cityInfoBO = queryCityInfoById(id);
        if (cityInfoBO == null) {
            return true;
        }
        Boolean removeResult = cityInfoRepository.removeCityInfoPOById(id);
        if (removeResult) {
            //由于是本地缓存，所以清除的只能是本机的缓存，但是整体时间为5min 基本不影响
            CITY_LOCAL_CACHE.invalidate(LocalCacheHelper.getCityInfoBOKey(cityInfoBO.getId()));
            String detailKey = SightKeyBuild.cityDetailKey(cityInfoBO.getId());
            redisService.del(detailKey);
            String parentIdKey = SightKeyBuild.cityParentIdListKey(cityInfoBO.getParentId());
            redisService.sRemove(parentIdKey, String.valueOf(id));
        }
        return removeResult;
    }

    @Override
    public CityInfoBO queryCityInfoById(Long id) {
        if (id == null || id < 0) {
            return null;
        }
        String cityInfoKey = LocalCacheHelper.getCityInfoBOKey(id);
        //查询本地缓存 - 5min过期时间
        CityInfoBO cityInfoBO = CITY_LOCAL_CACHE.getIfPresent(cityInfoKey);
        if (cityInfoBO != null) {
            return cityInfoBO;
        }
        String detailKey = SightKeyBuild.cityDetailKey(id);
        CityInfoBO redisData = JSONObject.parseObject(redisService.get(detailKey), CityInfoBO.class);
        if (redisData != null) {
            CITY_LOCAL_CACHE.put(cityInfoKey, redisData);
            return redisData;
        }

        log.info("CityInfoService#queryCityInfoById 城市信息获取本地缓存失败，查询DB获取数据 ,id ：【{}】", id);
        CityInfoPO cityInfoPO = cityInfoRepository.queryCityInfoPOById(id);
        if (cityInfoPO != null) {
            cityInfoBO = SightServiceConvert.convertToCityInfoBO(cityInfoPO);
            CITY_LOCAL_CACHE.put(cityInfoKey, cityInfoBO);
            redisService.set(detailKey, JSONObject.toJSONString(cityInfoBO));
        }

        return cityInfoBO;
    }

    @Override
    public List<CityInfoBO> searchCityInfoByName(String name) {
        if (StringUtils.isBlank(name)) {
            return null;
        }
        List<CityInfoPO> cityInfoPOS = cityInfoRepository.searchCityInfoByName(name);
        if (CollectionUtils.isEmpty(cityInfoPOS)) {
            return Lists.newArrayList();
        }
        return cityInfoPOS.stream().map(SightServiceConvert::convertToCityInfoBO).collect(Collectors.toList());
    }

    @Override
    public CityInfoBO queryCityInfoByName(String name) {
        if (StringUtils.isBlank(name)) {
            return null;
        }
        CityInfoPO cityInfoPO = cityInfoRepository.queryCityInfoByName(name);

        return SightServiceConvert.convertToCityInfoBO(cityInfoPO);
    }

    @Override
    public List<CityInfoBO> queryCityInfoByParentId(Long parentId, CityTypeEnum cityType) {
        if (parentId == null || parentId < 0) {
            return Collections.emptyList();
        }
        //查询非省份信息前，确认父级别Id是否存在
        if (!CityTypeEnum.PROVINCE.equals(cityType)) {
            CityInfoBO cityInfoBO = queryCityInfoById(parentId);
            if (cityInfoBO == null) {
                log.warn("CityInfoService#queryCityInfoByParentId 获取父级城市信息失败，当前父级Id不存在 parentId ：【{}】", parentId);
                return Collections.emptyList();
            }
        }
        String parentIdKey = SightKeyBuild.cityParentIdListKey(parentId);
        Set<String> strings = redisService.sMembers(parentIdKey);
        List<CityInfoBO> result = Lists.newArrayList();
        if (CollectionUtils.isEmpty(strings)) {
            //查询数据库
            List<CityInfoPO> cityInfoPOS = cityInfoRepository.queryCityInfoListByParentId(parentId);
            if (CollectionUtils.isEmpty(cityInfoPOS)) {
                return Collections.emptyList();
            }
            for (CityInfoPO cityInfoPO : cityInfoPOS) {
                redisService.sAdd(parentIdKey, String.valueOf(cityInfoPO.getId()));
                result.add(SightServiceConvert.convertToCityInfoBO(cityInfoPO));
            }
            return result;
        }
        //todo V1版本暂且这样实现，后续实现Map即可
        for (String s : strings) {
            CityInfoBO cityInfoBO = queryCityInfoById(Long.valueOf(s));
            if (cityInfoBO != null) {
                result.add(cityInfoBO);
            }
        }
        return result;
    }

    @Override
    public Boolean enableCityInfoById(Long cityId, Integer isShow) {
        if (cityId == null || cityId < 0 || isShow == null) {
            throw new BizException(BizErrorCodeEnum.PARAM_ERROR);
        }
        CityInfoBO cityInfoBO = queryCityInfoById(cityId);
        if (cityInfoBO == null) {
            log.error("CityInfoService#enableCityInfoById 启禁用城市信息失败 根据主键查询数据为空 cityId：【{}】,isShow:【{}】", cityId, isShow);
            throw new BizException(BizErrorCodeEnum.DATA_NOT_EXIST);
        }
        if (isShow.equals(cityInfoBO.getIsShow())) {
            return true;
        }
        cityInfoBO.setIsShow(isShow);
        return updateCityInfo(cityInfoBO);
    }

    @Override
    public PageResult<CityInfoBO> queryCityInfoByCondition(CityInfoConditionBO conditionBO) {
        if (conditionBO == null) {
            return new PageResult<>();
        }
        CityInfoCondition cityInfoCondition = SightServiceConvert.convertToCityInfoCondition(conditionBO);
        //父名称不存在时候不做处理
        if (StringUtils.isNotBlank(conditionBO.getParentName())) {
            CityInfoBO cityInfoBO = queryCityInfoByName(conditionBO.getParentName());
            if (cityInfoBO == null) {
                log.warn("CityInfoService#queryCityInfoByCondition 多条件查询，存在父名称，根据父名称查询数据不存在，不做处理 conditionBO：【{}】", JSONObject.toJSONString(conditionBO));
                throw new BizException(BizErrorCodeEnum.DATA_NOT_EXIST, "父名称不存在");
            }
            cityInfoCondition.setParentId(cityInfoBO.getId());
        }
        PageResult<CityInfoPO> pageResult = cityInfoRepository.queryCityInfoByCondition(cityInfoCondition);
        if (pageResult == null) {
            return new PageResult<>();
        }
        PageResult<CityInfoBO> result = new PageResult<>();
        result.setResults(SightServiceConvert.convertToCityInfoBOList(pageResult.getResults()));
        result.setCurrentIndex(pageResult.getCurrentIndex());
        result.setPageCount(pageResult.getPageCount());
        result.setTotal(pageResult.getTotal());
        result.setPageNum(pageResult.getPageNum());
        result.setHasNext(pageResult.isHasNext());
        result.setPageSize(pageResult.getPageSize());
        return result;
    }
}
